package com.example.mq.mqserver.datacenter;

import com.example.mq.common.MQException;
import com.example.mq.mqserver.core.Binding;
import com.example.mq.mqserver.core.Exchange;
import com.example.mq.mqserver.core.MQueue;
import com.example.mq.mqserver.core.Message;

import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

/**
 * 这个类给上层提供统一的硬盘操作
 * 封装消息文件操作和数据库操作
 */
public class DiskDataCenter {
    // 用来管理数据库中的数据
    private final DataBaseManager dataBaseManager = new DataBaseManager();
    // 用来管理文件中的数据
    private final MessageFileManager messageFileManager = new MessageFileManager();

    /**
     * 初始化 DataBaseManager
     */
    public void init() {
        dataBaseManager.init();
    }


    /**
     * 数据库中新增交换机
     * @param exchange  交换机
     */
    public void insertExchange(Exchange exchange) {
        dataBaseManager.insertExchange(exchange);
    }

    /**
     * 数据库删除交换机
     * @param exchangeName  交换机名
     */
    public void deleteExchange(String exchangeName) {
        dataBaseManager.deleteExchange(exchangeName);
    }

    /**
     * 查询数据库中所有的交换机
     */
    public List<Exchange> selectAllExchanges() {
        return dataBaseManager.selectAllExchanges();
    }

    /**
     * 数据库中新增队列
     * @param queue 队列
     */
    public void insertQueue(MQueue queue) throws IOException {
        // 新增队列时，不仅要在数据库中插入数据，还有给队列创建目录和文件，后续消息才有地方存储
        dataBaseManager.insertQueue(queue);
        messageFileManager.createQueueFiles(queue.getName());
    }

    /**
     * 数据库中删除队列
     * @param queueName     队列名
     */
    public void deleteQueue(String queueName) throws IOException {
        // 删除队列时，不仅要删除数据库的数据，还有把队列对应的目录和文件删除掉
        dataBaseManager.deleteQueue(queueName);
        messageFileManager.createQueueFiles(queueName);
    }

    /**
     *  查询数据库总所有的队列
     */
    public List<MQueue> selectAllQueues() {
        return dataBaseManager.selectAllQueues();
    }

    /**
     * 数据库中新增绑定
     * @param binding  绑定（交换机和队列的关联关系）
     */
    public void insertBinding(Binding binding) {
        dataBaseManager.insertBinding(binding);
    }

    /**
     * 数据库删除绑定
     * @param binding  绑定
     */
    public void deleteBinding(Binding binding) {
        dataBaseManager.deleteBinding(binding);
    }

    /**
     * 查询数据库中所有的绑定
     */
    public List<Binding> selectAllBindings() {
        return dataBaseManager.selectAllBindings();
    }

    /**
     * 将一个消息写入指定队列的数据文件
     * @param queue     队列名
     * @param message   消息
     */
    public void sendMessage(MQueue queue, Message message) throws IOException, MQException {
        messageFileManager.sendMessage(queue, message);
    }

    /**
     * 将一个消息从队列的数据文件中删除
     * @param queue     队列名
     * @param messageId   消息ID
     */
    public void deleteMessage(MQueue queue, String messageId, MemoryDataCenter memoryDataCenter) throws IOException, ClassNotFoundException, MQException {
        synchronized (queue) {
            // 需要提前加锁，因为 GC 之后，消息在文件中的位置就变了
            // GC 之后，要更新消息在文件中的位置后，才能通过 id 获取到更新后的 消息对象

            // 删除硬盘中的消息
            messageFileManager.deleteMessage(queue, memoryDataCenter.getMessage(messageId));
            // 删除后，判断是否要 GC
            if (messageFileManager.checkGC(queue.getName())) {
                messageFileManager.gc(queue, memoryDataCenter);
            }
        }
    }

    /**
     * 根据队列名，加载消息文件中的消息数据
     * @param queueName 队列名
     * @return          消息链表
     */
    public LinkedList<Message> loadAllMessageFromQueue(String queueName) throws IOException, MQException, ClassNotFoundException {
        return messageFileManager.loadAllMessageFromQueue(queueName);
    }
}
